/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package estsapp.expertos;

import estsapp.entidades.ClasificacionProducto;
import estsapp.entidades.DetalleProducto;
import estsapp.entidades.Material;
import estsapp.entidades.ModeloGestionStock;
import estsapp.entidades.OrdenDeProduccion;
import estsapp.entidades.Precio;
import estsapp.entidades.ProductoTerminado;
import estsapp.expertos.excepciones.ABMProductoTerminadoException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import persistencia.Conexion;
import persistencia.Fachada;
import persistencia.exceptions.ExcepcionFachada;

/**
 *
 * @author Juan
 */
public class ExpertoABMProductoTerminado implements Experto {

//    public List buscarMaterial() {
//        return ((ExpertoABMMaterial) FabricaExpertos.getInstancia().getExperto(FabricaExpertos.expertos.ABM_TIPO_MATERIAL)).buscarMateriales();
//    }
    public void darDeBaja(ProductoTerminado productoTerminado) throws ABMProductoTerminadoException, ExcepcionFachada {

        Criteria criterioOrdenesActivas = Fachada.getInstancia().crearCriterio(OrdenDeProduccion.class);
        criterioOrdenesActivas.createCriteria("listaDetalles").add(Restrictions.eq("productoTerminado", productoTerminado));
        criterioOrdenesActivas.add(Restrictions.eq("ordenActiva", true));
        List ordenes = Fachada.getInstancia().buscar(OrdenDeProduccion.class, criterioOrdenesActivas);

        if (!ordenes.isEmpty()) {
            throw new ABMProductoTerminadoException("Producto con orden de produccion pendiente");
        } else {
            productoTerminado.setVigente(false);
            try {
                Conexion.getInstancia().iniciarTX();
                Fachada.getInstancia().guardar(productoTerminado);
                Conexion.getInstancia().confirmarTx();
            } catch (ExcepcionFachada ex) {
                Conexion.getInstancia().deshacerTx();
                throw ex;
            }
        }
    }

    public void guardar(ProductoTerminado productoTerminado) throws ABMProductoTerminadoException, ExcepcionFachada {
        validarProductoTerminado(productoTerminado);
        validarExistencia(productoTerminado);
        String codigo;
        codigo = ((ExpertoCodigoBrisch) FabricaExpertos.getInstancia().getExperto(FabricaExpertos.expertos.GENERAR_CODIGO)).generarCodigo(productoTerminado);
        productoTerminado.setCodigo(codigo);

        Float costo = calcularCostoProducto(productoTerminado);
        productoTerminado.setCosto(costo);
        Float precio = calcularPrecio(productoTerminado);
        productoTerminado.setPrecio(precio);
        productoTerminado.setVigente(true);
        try {
            Conexion.getInstancia().iniciarTX();
            Fachada.getInstancia().guardar(productoTerminado);
            Conexion.getInstancia().confirmarTx();
        } catch (ExcepcionFachada ex) {
            Conexion.getInstancia().deshacerTx();
            Logger.getLogger(ExpertoABMProductoTerminado.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public List buscarClasificacionesProductos() {
        Criteria vigente = Fachada.getInstancia().crearCriterio(ClasificacionProducto.class).add(Restrictions.eq("vigente", true));
        vigente.createCriteria("categoriaBrisch").add(Restrictions.eq("codigo", "4"));

        return Fachada.getInstancia().buscar(ClasificacionProducto.class, vigente);
    }

    public List buscarProductosTerminados() {
        return Fachada.getInstancia().buscar(ProductoTerminado.class, null);
    }

    public List buscarProductosVigentes() {
        Criteria vigente = Fachada.getInstancia().crearCriterio(ProductoTerminado.class).add(Restrictions.eq("vigente", true));
        return Fachada.getInstancia().buscar(ProductoTerminado.class, vigente);
    }

    public List buscarProductosTerminados(String codigo) {
        Criteria criterioCodigo = Fachada.getInstancia().crearCriterio(ProductoTerminado.class);
        criterioCodigo.add(Restrictions.ilike("codigo", codigo));
        return Fachada.getInstancia().buscar(ProductoTerminado.class, criterioCodigo);
    }

    private void validarProductoTerminado(ProductoTerminado producto) throws ABMProductoTerminadoException {

        if (producto.getPorcentajeGanancia() == null) {
            throw new ABMProductoTerminadoException("Falta porcentaje de ganancia");
        }

        if (producto.getDetalles().isEmpty()) {
            throw new ABMProductoTerminadoException("No ha cargado detalles");
        } else {
            List detalles = producto.getDetalles();
            for (Object object : detalles) {
                DetalleProducto detalle = (DetalleProducto) object;

                if (detalle.getCantidad() == 0) {
                    throw new ABMProductoTerminadoException("No ha cargado correctamente la cantidad");
                }
                if (detalle.getUnidadMedida().length() == 0) {
                    throw new ABMProductoTerminadoException("No ha cargado correctamente la unidad de medida");
                }
                if (detalle.getPieza() == null) {
                    throw new ABMProductoTerminadoException("No se ha cargado pieza");
                }

            }
        }
        if(producto.getStock().getModelo() == null){
                    throw new ABMProductoTerminadoException("Debe Seleccionar Modelo Gestión Stock");
        }
    }

    private void validarExistencia(ProductoTerminado producto) throws ABMProductoTerminadoException {
        if (producto.getId() != null) {
            return;
        }
        Criteria codigoProducto = Fachada.getInstancia().crearCriterio(ProductoTerminado.class);
        codigoProducto.add(Restrictions.eq("nombre", producto.getNombre()).ignoreCase());
        if (!Fachada.getInstancia().buscar(ProductoTerminado.class, codigoProducto).isEmpty()) {
            throw new ABMProductoTerminadoException("Producto ya existente");
        }
    }

    private Float calcularCostoProducto(ProductoTerminado productoTerminado) {
        List detallesProducto = productoTerminado.getDetalles();
        Float costoProducto = 0f;

        for (Object object : detallesProducto) {
            Float costoMaterial;
            DetalleProducto detalle = (DetalleProducto) object;
            Float cantidad = detalle.getCantidad();

            Float precioUnitario = 0f;
            if (detalle.getPieza() instanceof Material) {
                precioUnitario = calcularCostoXUnidad((Material) detalle.getPieza());
            } else if (detalle.getPieza() instanceof ProductoTerminado) {
                precioUnitario = ((ProductoTerminado) detalle.getPieza()).getCosto();
            }
            costoMaterial = cantidad * precioUnitario;
            costoProducto += costoMaterial;
        }
        return costoProducto;
    }

    private Float calcularCostoXUnidad(Material material) {

        Float menorPrecio = material.getPrecios().get(0).getMonto();
        for (Precio precio : material.getPrecios()) {
            if (precio.getMonto() < menorPrecio) {
                menorPrecio = precio.getMonto();
            }
        }
        Float costoUnitario = menorPrecio / material.getTamaño();
        return costoUnitario;
    }

    private Float calcularPrecio(ProductoTerminado productoTerminado) {
        Float precio;
        Float procentajeGanancia = productoTerminado.getPorcentajeGanancia() / 100;
        precio = (procentajeGanancia * productoTerminado.getCosto()) + productoTerminado.getCosto();
        return precio;
    }

    public List buscarModelosGestionStock() {
        return Fachada.getInstancia().buscar(ModeloGestionStock.class, null);
    }
    
}
